use crate::config::httpie_config::{Opts, Post, Put, Subcommand};
use colored::*;
use mime::Mime;
use reqwest::{header, Client, Response, Url};
use std::collections::HashMap;

#[tokio::main]
pub async fn request(option: &Opts) -> Result<(), Box<dyn std::error::Error>> {
    let mut headers = header::HeaderMap::new();
    // 为我们的 HTTP 客户端添加一些缺省的 HTTP 头
    headers.insert("X-POWERED-BY", "Rust".parse()?);
    headers.insert(header::USER_AGENT, "Rust Httpie".parse()?);
    // 生成一个 HTTP 客户端
    let client = Client::builder().default_headers(headers).build()?;

    let result = match &option.subcmd {
        Subcommand::Get(args) => get(client, args.url.parse()?).await?,
        Subcommand::Post(args) => post(client, args).await?,
        Subcommand::Put(args) => put(client, args).await?,
        Subcommand::Delete(args) => delete(client, args.url.parse()?).await?,
    };
    Ok(result)
}

async fn get(client: Client, url: Url) -> Result<(), Box<dyn std::error::Error>> {
    let resp = client.get(url).send().await?;
    Ok(print_resp(resp).await?)
}

async fn post(client: Client, args: &Post) -> Result<(), Box<dyn std::error::Error>> {
    let mut body = HashMap::new();
    for kv in args.body.iter() {
        body.insert(&kv.key, &kv.value);
    }
    let resp = client.post(&args.url).json(&body).send().await?;
    Ok(print_resp(resp).await?)
}

async fn delete(client: Client, url: Url) -> Result<(), Box<dyn std::error::Error>> {
    let resp = client.get(url).send().await?;
    Ok(print_resp(resp).await?)
}

async fn put(client: Client, args: &Put) -> Result<(), Box<dyn std::error::Error>> {
    let mut body = HashMap::new();
    for kv in args.body.iter() {
        body.insert(&kv.key, &kv.value);
    }
    let resp = client.post(&args.url).json(&body).send().await?;
    Ok(print_resp(resp).await?)
}

async fn print_resp(resp: Response) -> Result<(), Box<dyn std::error::Error>> {
    print_status(&resp);
    print_headers(&resp);
    let mime = get_content_type(&resp);
    let body = resp.text().await?;
    print_body(mime, &body);
    Ok(())
}

fn print_body(m: Option<Mime>, body: &String) {
    match m {
        // 对于 "application/json" 我们 pretty print
        Some(mime) if mime == mime::APPLICATION_JSON => {
            println!("{}", jsonxf::pretty_print(body).unwrap().cyan());
        }
        // 其他 MIME 类型的处理
        Some(mime) if mime == mime::TEXT_HTML => {
            println!("{}", body.cyan());
        }
        None | _ => {
            println!("{}", body);
        }
    }
}

fn print_status(resp: &Response) {
    let status = format!("{:?} {}", resp.version(), resp.status()).blue();
    println!("{}\n", status);
}

fn print_headers(resp: &Response) {
    for (key, value) in resp.headers() {
        println!("{}: {:?}", key.to_string().green(), value);
    }
    println!("\n");
}

fn get_content_type(resp: &Response) -> Option<Mime> {
    resp.headers()
        .get(header::CONTENT_TYPE)
        .map(|v| v.to_str().unwrap().parse().unwrap())
}
